// import LabelComponent from "@components/Apps/Common/LeaderLineLabel/LabelComponent";
import LabelComponent from "@runtime/UnityEngineBridge/LabelComponent";
import ModalComponent, { ModalOptions } from "@runtime/module/Modal/ModalComponent";
import TooltipComponent from "@runtime/UnityEngineBridge/TooltipComponent";
import AppSettings from "@runtime/base/AppSettings";
import { ClientMessage } from "@runtime/generate/message/ClientMessage";
import WebWindowComponent from "@runtime/module/WebWindow/WebWindowComponent";
import RouterComponent, { RouteConfig } from "@runtime/module/Router/RouterComponent";
import TimerComponent from "@runtime/module/Timer/TimerComponent";
import WebSocketComponent from "@runtime/module/WebSocket/WebSocketComponent";
import AnimatorComponent from "@runtime/UnityEngineBridge/AnimatorComponent";
import AssetLoaderComponent from "@runtime/UnityEngineBridge/AssetLoaderComponent";
import GameObjectComponent from "@runtime/UnityEngineBridge/GameObjectComponent";
import HighlightingSystemComponent from "@runtime/UnityEngineBridge/HighlighterComponent";
import InteractableObjectComponent from "@runtime/UnityEngineBridge/InteractableObjectComponent";
import OrbitCameraComponent from "./OrbitCameraComponent";
import PostProcessLayerComponent from "@runtime/UnityEngineBridge/PostProcessLayerComponent";
import { computed, ComputedRef, reactive, Ref, ref, UnwrapNestedRefs, watch, WatchStopHandle } from "vue";
// import IntelligentConnectedVehicleSettings from "../IntelligentConnectedVehicleSettings";
import LaserRadarSetting from "./LaserRadarSetting";

function ToMap<TKey, TValue>(sources: Array<TValue>, keySelector: (element: TValue) => TKey): Map<TKey, TValue> {
    const map = new Map<TKey, TValue>();
    for (const element of sources) {
        map.set(keySelector(element), element);
    }
    return map;
}

export const ModuleConfigs: ComputedRef<Array<{ Id: number, Name: string, Description: string, Icon: string }>> = computed(() => {
    const configs = [
        { Id: 1, Name: 'Introduction', Description: '系统简介', Icon: 'ic_menu_1' },
        { Id: 2, Name: 'Structure', Description: '结构认知', Icon: 'ic_menu_2' },
        { Id: 3, Name: 'Principle', Description: '工作原理', Icon: 'ic_menu_3' },
        { Id: 4, Name: 'Install', Description: '安装', Icon: 'ic_menu_4' },
        { Id: 5, Name: 'Calibration', Description: '标定', Icon: 'ic_menu_5' },
        { Id: 6, Name: 'Protocol', Description: '协议解读', Icon: 'ic_menu_6' },
    ];

    if (LaserRadarComponent.Default.Status.value == '设备已连接') {
        configs.add({ Id: 7, Name: 'Usage', Description: '应用', Icon: 'ic_menu_7' },);
    }

    return configs;
});

class CoroutineLock implements Chaos.IDisposable {
    public Type: number = 0;
    public Key: number = 0;
    public Count: number = 0;

    private isCompleted: boolean = false;
    private task?: (value: void | PromiseLike<void>) => void;

    constructor(type: number, key: number, count: number) {
        this.Type = type;
        this.Key = key;
        this.Count = count;
    }

    async WaitAsync(): Promise<void> {
        if (this.isCompleted) {
            return;
        }

        return new Promise(resolve => this.task = resolve);
    }

    Dispose() {
        LaserRadarComponent.Default.IsLoading.value = false;

        this.Type = 0;
        this.Key = 0;
        this.Count = 0;

        if (this.task != undefined) {
            this.task();
        }

        this.isCompleted = true;
    }
}

// class CoroutineLock implements IDisposable {
//     public Type: number = 0;
//     public Key: number = 0;
//     public Count: number = 0;

//     private isCompleted: boolean = false;
//     private task?: (value: void | PromiseLike<void>) => void;

//     constructor(type: number, key: number, count: number) {
//         this.Type = type;
//         this.Key = key;
//         this.Count = count;
//     }

//     async WaitAsync(): Promise<void> {
//         if (this.isCompleted) {
//             return;
//         }

//         return new Promise(resolve => this.task = resolve);
//     }

//     Dispose() {
//         LaserRadarComponent.Default.IsLoading.value = false;

//         this.Type = 0;
//         this.Key = 0;
//         this.Count = 0;

//         if (this.task != undefined) {
//             this.task();
//         }

//         this.isCompleted = true;
//     }
// }

/**
 * 激光雷达组件
 */
export default class LaserRadarComponent {
    private static instance: LaserRadarComponent = new LaserRadarComponent();
    /**
     * 激光雷达组件
     */
    public static get Default(): LaserRadarComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.Module, this.OnModuleChangedAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public Title: Ref<string> = ref('智能网联汽车激光雷达系统');

    public Status: Ref<string> = ref('');

    /**
     * 当前模块
     */
    public Module: Ref<string> = ref('Introduction');

    public IsLoading: Ref<boolean> = ref(false);

    private watchStopHandles: Array<WatchStopHandle> = [];
    private hasInitialized: boolean = false;
    public async Awake(): Promise<void> {
        // // 等待时长
        // await TimerComponent.Default.WaitAsync(1000);

        // // 切换至指定传感器路由
        // const url = `/${AppSettings.Default.Type}/${IntelligentConnectedVehicleSettings.Default.SensorType}/${this.Module.value}`;
        // await RouterComponent.Default.NavigateToAsync(url);

        ModalComponent.Default.AddDynamicModules(import.meta.glob('/src/components/Apps/LaserRadar/Exit.vue'));
        ModalComponent.Default.AddDynamicModules(import.meta.glob('/src/components/Apps/LaserRadar/MessageBox.vue'));

        this.Module.value = 'Home';

        // 启动Unity
        const configuration = JSON.stringify({
            LocalAddress: AppSettings.Default.LocalAddress,
            ShortName: 'P10052',
            Type: 4,
        });
        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.EmbeddedApplicationLaunchRequest('../EmbeddedApplication/Unity/Mix.Unity.exe', configuration));

        // 获取Unity版本状态
        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.EmbeddedApplicationVersionRequest());

        // if (!this.hasInitialized) {
        //     await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.MillimeterWaveRadarDeviceInitializationRequest(
        //         LaserRadarSetting.Default.DeviceType,
        //         LaserRadarSetting.Default.DeviceIndex,
        //         LaserRadarSetting.Default.CanIndex,
        //     ));

        //     this.hasInitialized = true;
        // }

        this.Module.value = 'Introduction';
    }

    public async Destroy(): Promise<void> {
    }

    /**
    * 模块发生变化时
    * @param value 模块
    */
    private async OnModuleChangedAsync(value: string): Promise<void> {
        // 切换至指定传感器路由
        // const url = `/${AppSettings.Default.Type}/${IntelligentConnectedVehicleSettings.Default.SensorType}/${value}`;
        const url = `/${AppSettings.Default.Type}/${value}`;
        await RouterComponent.Default.NavigateToAsync(url);
    }

    private map: Map<number, Array<CoroutineLock>> = new Map();

    public async WaitAsync(type: number = 0, key: number = 0): Promise<CoroutineLock> {
        this.IsLoading.value = true;

        if (!this.map.has(key)) {
            this.map.set(key, []);
        }

        const locks = this.map.get(key)!;
        const lock = new CoroutineLock(type, key, locks.length);
        locks.add(lock);
        if (locks.length < 2) {
            return new Promise(resolve => resolve(lock));
        } else {
            const previous = locks[locks.length - 2];
            await previous.WaitAsync();
            return new Promise(resolve => resolve(lock));
        }
    }
}



/**
 * 系统简介
 */
export class IntroductionComponent {
    private static instance: IntroductionComponent = new IntroductionComponent();
    /**
     * 系统简介
     */
    public static get Default(): IntroductionComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public Title: string = '激光雷达系统';

    public Description: string = `激光雷达系统，即系统包含卫星定位系统（GPS(全球定位系统)/北斗卫星导航系统/GNSS（全球导航卫星系统））和惯性定向定位导航系统（INS）的定向定位导航系统。根据INS和卫星的导航功能互补的特点，以适当的方法将两者组合来提高系统的整体导航精度及导航性能以及空中对准和再对准的能力。`;

    // public Img: string = '/static/LaserRadar/激光雷达简介.png';

    private instanceId: number = 0;
    
    private watchStopHandles: Array<WatchStopHandle> = [];

    public async Awake(): Promise<void> {        
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetTransparentBackground();
        TooltipComponent.Default.Clear();

        if (this.instanceId == 0) {
            this.instanceId = await AssetLoaderComponent.Default.LoadAsync('Assets/R00034/ICV/Product/Introduction/激光雷达系统/激光雷达系统.prefab');
                                                                            
        } else {            
            await GameObjectComponent.Default.ShowAsync(this.instanceId);
        }

        await OrbitCameraComponent.Default.UpdateAsync([0, 0.18, -0.18], 90, 20, 3, 0.1, 10);

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        await GameObjectComponent.Default.HideAsync(this.instanceId);

        lock.Dispose()
    }
}

class StructureOptionConfig {
    Id: number;
    InstanceId: number;
    Type: string;
    Asset: string;
    /**
     * 动画状态
     */
    State: string;
    Description: string;
    ViewId: number;

    constructor(id: number, instanceId: number, type: string, asset: string, state: string, description: string, viewId: number) {
        this.Id = id;
        this.InstanceId = instanceId;
        this.Type = type;
        this.Asset = asset;
        this.State = state;
        this.Description = description;
        this.ViewId = viewId;
    }
}

class StructureConfig {
    Id: number;
    Name: string;
    Options: Array<StructureOptionConfig>;

    constructor(id: number, name: string, options: Array<StructureOptionConfig>) {
        this.Id = id;
        this.Name = name;
        this.Options = options;
    }

    public static Copy(source: StructureConfig, destination: StructureConfig): void {
        destination.Id = source.Id;
        destination.Name = source.Name;
        destination.Options.clear();
        destination.Options.addRange(source.Options);
    }
}

/**
 * 结构认知
 */
export class StructureComponent {
    private static instance: StructureComponent = new StructureComponent();
    /**
     * 结构认知
     */
    public static get Default(): StructureComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.Id, this.OnIdChangedAsync.bind(this)));
        this.watchStopHandles.push(watch(this.OptionId, this.OnOptionIdChangedAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    /**
     * 结构配置
     */
    public Configs: UnwrapNestedRefs<Array<StructureConfig>> = reactive([
        new StructureConfig(1, '激光雷达系统', [
            new StructureOptionConfig(1, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达系统.prefab', '', '', 3001),
            new StructureOptionConfig(2, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达系统.prefab', '', '', 3001),
        ]),
        new StructureConfig(2, '激光雷达', [
            new StructureOptionConfig(3, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达.prefab', '', '', 3002),
            new StructureOptionConfig(4, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达.prefab', '', '', 3002),
        ]),
        new StructureConfig(3, '激光雷达处理器', [
            new StructureOptionConfig(5, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达处理器.prefab', '', '', 3003),
            new StructureOptionConfig(6, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达处理器.prefab', '', '', 3003),
        ]),
        new StructureConfig(4, '激光雷达数据传输线', [
            new StructureOptionConfig(7, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达数据传输线.prefab', '', '', 3004),
            new StructureOptionConfig(8, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达数据传输线.prefab', '', '', 3004),
        ]),
        new StructureConfig(5, '激光雷达电源', [
            new StructureOptionConfig(9, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达电源.prefab', '', '', 3005),
            new StructureOptionConfig(10, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/激光雷达电源.prefab', '', '', 3005),
        ]),
        new StructureConfig(6, '计算平台', [
            new StructureOptionConfig(11, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/计算平台.prefab', '', '', 3006),
            new StructureOptionConfig(12, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/计算平台.prefab', '', '', 3006),
        ]),
        new StructureConfig(7, '计算机平台电源', [
            new StructureOptionConfig(13, 0, '结构', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/计算机平台电源.prefab', '', '', 3007),
            new StructureOptionConfig(14, 0, '组成', 'Assets/R00034/ICV/Product/Structure/激光雷达系统/计算机平台电源.prefab', '', '', 3007),
        ]),
    ]);

    /**
     * 当前结构配置
     */
    public Config: UnwrapNestedRefs<StructureConfig> = reactive(new StructureConfig(0, '', []));

    /**
     * 当前结构配置Id
     */
    public Id: Ref<number> = ref(0);

    /**
     * 当前结构配置选项Id
     */
    public OptionId: Ref<number> = ref(0);

    public Description: Ref<string> = ref('');

    private watchStopHandles: Array<WatchStopHandle> = [];

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetTransparentBackground();
        TooltipComponent.Default.Clear();

        if (this.Id.value == 0) {
            this.Id.value = 1;
        }

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        LabelComponent.Default.Clear();
        await HighlightingSystemComponent.Default.ClearAsync();

        this.Id.value = 0;

        const option = this.Config.Options.find(option => option.Id == this.OptionId.value);
        if (option != null && option.InstanceId != 0) {
            await GameObjectComponent.Default.HideAsync(option.InstanceId);
        }

        lock.Dispose()
    }

    /**
     * Id发生变化时
     * @param value 值
     */
    private async OnIdChangedAsync(value: number): Promise<void> {
        // 更新结构数据
        const config = this.Configs.find(config => config.Id == value);
        if (config != null) {
            // 隐藏当前模型
            const instanceId = this.Config.Options.find(option => option.Id == this.OptionId.value)?.InstanceId ?? 0;
            if (instanceId != 0) {
                await GameObjectComponent.Default.HideAsync(instanceId);
            }

            // 复制结构配置
            StructureConfig.Copy(config, this.Config);

            // 更新选项
            this.OptionId.value = this.Config.Options[0].Id;
        }
    }

    /**
     * 隐藏上一选项对应物体
     * @param value 选项Id
     */
    private async HidePreviousOptionGameObject(value: number): Promise<void> {
        const option = this.Config.Options.find(option => option.Id == value);
        if (import.meta.env.PROD && option != null && option.InstanceId != 0) {
            await GameObjectComponent.Default.HideAsync(option.InstanceId);
        }
    }

    /**
     * 选项发生变化时
     * @param value 值
     */
    private async OnOptionIdChangedAsync(value: number, previous: number): Promise<void> {
        LaserRadarComponent.Default.IsLoading.value = true;

        await this.HidePreviousOptionGameObject(previous);

        const option = this.Config.Options.find(option => option.Id == value);
        if (import.meta.env.PROD) {
            if (option != null) {
                // 清空标签
                LabelComponent.Default.Clear();

                // 重置高亮
                await HighlightingSystemComponent.Default.ClearAsync();

                if (option.InstanceId == 0) {
                    const volume = await PostProcessLayerComponent.Default.AddVolumeAsync();
                    const effect = await PostProcessLayerComponent.Default.AddKawaseBlurAsync(volume);

                    // 加载模型
                    option.InstanceId = await AssetLoaderComponent.Default.LoadAsync(option.Asset);

                    await PostProcessLayerComponent.Default.RemoveEffectAsync(volume, effect);
                    await PostProcessLayerComponent.Default.RemoveVolumeAsync(volume);
                } else {
                    // 显示当前选项模型
                    await GameObjectComponent.Default.ShowAsync(option.InstanceId);
                }
 
                // 切换视角
                await OrbitCameraComponent.Default.ApplyConfigAsync(option.ViewId);

                if (option.State != '') {
                    // 播放动画
                    await AnimatorComponent.Default.PlayAsync(option.InstanceId, option.State, 1000);
                }

                if (option.Type == '组成') {
                    // 加载标签
                    await LabelComponent.Default.LoadAsync(option.InstanceId);
                }
                this.Description.value = option.Description;
            } else {
                window.Logger.Error(`对应选项(Id:${value})不存在`);
            }
        }
        this.Description.value = option?.Description ?? '';

        LaserRadarComponent.Default.IsLoading.value = false;
    }
}

class PrincipleConfig {
    Id: number;
    InstanceId: number;
    Name: string;
    Asset: string;
    /**
     * 动画状态
     */
    State: string;
    Description: string;
    ViewId: number;

    constructor(id: number, instanceId: number, name: string, asset: string, state: string, description: string, viewId: number) {
        this.Id = id;
        this.InstanceId = instanceId;
        this.Name = name;
        this.Asset = asset;
        this.State = state;
        this.Description = description;
        this.ViewId = viewId;
    }
}

/**
 * 工作原理
 */
export class PrincipleComponent {
    private static instance: PrincipleComponent = new PrincipleComponent();
    /**
     * 工作原理
     */
    public static get Default(): PrincipleComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.Id, this.OnIdChangedAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    /**
    * 当前原理配置
    */
    public Configs: UnwrapNestedRefs<Array<PrincipleConfig>> = reactive([
        new PrincipleConfig(
            1,
            0,
            '激光雷达系统原理',
            'Assets/R00034/ICV/Product/Principle/激光雷达系统/激光雷达系统原理.prefab',
            
            '',
            '毫米波雷达测方位角时，需要多个接收天线，发射无线电波，多个天线同时接收回波，因天线在不同的位置，接收到回波的时间会有所不同，根据接收回波的相位差，和多个天线之间的距离差，根据三角函数，就可以计算出障碍物的方位角。',
            1012),
    ]);

    /**
     * 当前原理配置Id
     */
    public Id: Ref<number> = ref(0);

    public Description: Ref<string> = ref('');
    public DescriptionAudioAccessibility: Ref<boolean> = ref(true);
    private watchStopHandles: Array<WatchStopHandle> = [];

    private instanceIds: Array<number> = [];

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetTransparentBackground();
        TooltipComponent.Default.Clear();

        if (this.instanceIds.length == 0) {
            await this.LoadAsync(this.Configs.map(config => config.Asset));
            this.Id.value = this.Configs[0].Id;
        } else {
            const config = this.Configs.find(config => config.Id == this.Id.value)!;
            await GameObjectComponent.Default.ShowAsync(config.InstanceId);
        }

        await OrbitCameraComponent.Default.ApplyConfigAsync(3025);

        //lock.Dispose();
        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();
        await GameObjectComponent.Default.HideAsync(this.instanceIds);
        //lock.Dispose();
        lock.Dispose();
    }

    private async LoadAsync(assets: Array<string>): Promise<void> {
        if (import.meta.env.DEV) {
            return;
        }

        const volume = await PostProcessLayerComponent.Default.AddVolumeAsync();
        const effect = await PostProcessLayerComponent.Default.AddKawaseBlurAsync(volume);

        this.instanceIds.clear();
        this.instanceIds.addRange(await AssetLoaderComponent.Default.LoadAllAsync(assets));

        await PostProcessLayerComponent.Default.RemoveEffectAsync(volume, effect);
        await PostProcessLayerComponent.Default.RemoveVolumeAsync(volume);

        for (let index = 0; index < this.instanceIds.length; ++index) {
            const instanceId = this.instanceIds[index];
            this.Configs[index].InstanceId = instanceId;
        }

        await GameObjectComponent.Default.HideAsync(this.instanceIds);
    }

    /**
     * 原理Id发生变化时
     * @param value 值
     */
    private async OnIdChangedAsync(value: number, previous: number): Promise<void> {
        // 更新结构数据
        const config = this.Configs.find(config => config.Id == value);
        const previousConfig = this.Configs.find(config => config.Id == previous);
        if (import.meta.env.PROD) {
            if (config != null) {
                if (previousConfig != null) {
                    await GameObjectComponent.Default.HideAsync(previousConfig.InstanceId);
                }

                await GameObjectComponent.Default.ShowAsync(config.InstanceId);
            } else {
                window.Logger.Error(`对应选项(Id:${value})不存在`);
            }
        }

        this.Description.value = config?.Description ?? '';
    }
}

class InstallStepConfig {
    Id: number;
    Name: string;
    Asset: string;

    constructor(id: number, name: string, asset: string) {
        this.Id = id;
        this.Name = name;
        this.Asset = asset;
    }
}

class InstallViewConfig {
    Id: number;
    Name: string;
    Icon: string;

    constructor(id: number, name: string, icon: string) {
        this.Id = id;
        this.Name = name;
        this.Icon = icon;
    }
}

/**
 * 安装组件
 */
export class InstallComponent {
    private static instance: InstallComponent = new InstallComponent();
    /**
     * 安装组件
     */
    public static get Default(): InstallComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.ViewId, this.OnViewChangedAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public StepDescriptions: Array<string> = [
        '1. 安装惯导模块系统',
        '2. 安装卫星主天线',
        '3. 安装卫星副天线',
        '4. 安装计算平台',
        '5. 安装卫星主天线线束',
        '6. 安装卫星副天线线束',
        '7. 安装惯导模型系统线束',
        '8. 安装组合惯导系统数据通讯线',
    ];

    /**
     * 步骤提示
     */
    public StepTips: Array<InstallStepConfig> = [
        new InstallStepConfig(1, '步骤一', 'Step001.png'),
        new InstallStepConfig(2, '步骤二', 'Step002.png'),
        new InstallStepConfig(3, '步骤三', 'Step003.png'),
        new InstallStepConfig(4, '步骤四', 'Step004.png'),
    ];

    /**
     * 视角配置
     */
    public Views: Array<InstallViewConfig> = [
        new InstallViewConfig(3011, '目标', 'ic_mubiao'),
        new InstallViewConfig(3012, '零件桌', 'ic_lingjianzhuo'),
        new InstallViewConfig(3013, '车顶', 'ic_cheding'),
        new InstallViewConfig(3014, '车前', 'ic_cheqian'),
    ];

    public CanRecyclePart: ComputedRef<boolean> = computed(() => this.ViewId.value == 1009);

    public ViewId: Ref<number> = ref(0);

    public StepState: Ref<boolean> = ref(false);

    private watchStopHandles: Array<WatchStopHandle> = [];

    private instanceIds: Array<number> = [];

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetTransparentBackground();
        TooltipComponent.Default.Clear();

        if (import.meta.env.PROD) {
            if (this.instanceIds.length == 0) {
                await this.LoadAsync([
                    'Assets/R00034/ICV/Product/DisassemblyAndAssembly/激光雷达系统/零件桌.prefab',
                    'Assets/R00034/ICV/Product/DisassemblyAndAssembly/激光雷达系统安装/激光雷达系统安装.prefab'
                ]);
            } else {
                await GameObjectComponent.Default.ShowAsync(this.instanceIds);
            }

            await OrbitCameraComponent.Default.ApplyConfigAsync(this.Views[0].Id);
        }

        this.ViewId.value = this.Views[0].Id;

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        this.StepState.value = false;
        this.ViewId.value = 1;

        await GameObjectComponent.Default.HideAsync(this.instanceIds);

        lock.Dispose();
    }

    private async LoadAsync(assets: Array<string>): Promise<void> {
        const volume = await PostProcessLayerComponent.Default.AddVolumeAsync();
        const effect = await PostProcessLayerComponent.Default.AddKawaseBlurAsync(volume);

        this.instanceIds.clear();
        this.instanceIds.addRange(await AssetLoaderComponent.Default.LoadAllAsync(assets));

        await PostProcessLayerComponent.Default.RemoveEffectAsync(volume, effect);
        await PostProcessLayerComponent.Default.RemoveVolumeAsync(volume);

        await InteractableObjectComponent.Default.LoadAsync(['LaserRadar', 'LaserRadarLocation']);
    }

    /**
     * 安装视角发生变化
     * @param viewId 值
     */
    private async OnViewChangedAsync(viewId: number): Promise<void> {
        if (import.meta.env.PROD) {
            OrbitCameraComponent.Default.ApplyConfigAsync(viewId);
        } else {
            window.Logger.Debug(`视角切换: ${viewId}`);
        }
    }

    public async RecycleAsync(id: number, instanceId: number): Promise<void> {
        InteractableObjectComponent.Default.Remove(id);
        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.UncheckInteractableObjectRequest(id, instanceId));
        InteractableObjectComponent.Default.Choices.value.length -= 1        
    }
}

class CalibrationConfig {
    Id: number;
    Name: string;
    Asset: string;
    Options: Array<string>;
    ViewId: number;

    Descriptions: Map<string, string>;
    Records: Map<string, number>;

    InstanceId: number;
    Origin: number;
    ShowOrigin: boolean;
    Values: Array<number>;

    constructor(id: number, name: string, asset: string, options: Array<string>, viewId: number, descriptions: Map<string, string>, records: Map<string, number>, instanceId: number = 0, origin: number = 0, showOrigin: boolean = false, values: Array<number> = []) {
        this.Id = id;
        this.Name = name;
        this.Asset = asset;
        this.Options = options;
        this.ViewId = viewId;

        this.Descriptions = descriptions;
        this.Records = records;

        this.InstanceId = instanceId;
        this.Origin = origin;
        this.ShowOrigin = showOrigin;
        this.Values = values;
    }
}

/**
 * 标定组件
 */
export class CalibrationComponent {
    private static instance: CalibrationComponent = new CalibrationComponent();
    /**
     * 标定组件
     */
    public static get Default(): CalibrationComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.Id, this.OnIdChangedAsync.bind(this)));
        this.watchStopHandles.push(watch(this.ShowOrigin, this.OnOriginStateChangedAsync.bind(this)));
        this.watchStopHandles.push(watch(this.ShowTips, this.OnPopupChangedAsync.bind(this)));
        this.watchStopHandles.push(watch(this.ShowForms, this.OnPopupChangedAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public Configs: UnwrapNestedRefs<Array<CalibrationConfig>> = reactive([
        new CalibrationConfig(1, '车辆坐标系标定',
            'Assets/R00034/ICV/Product/Calibration/激光雷达标定/激光雷达车辆坐标系标定.prefab',
            ['显示原点', '标定说明', '参数标定'],
            3015,
            new Map<string, string>([
                ["Car_W", "车辆左侧至右侧距离:"],
                ["Car_L", "车辆前侧至后侧距离:"],
                ["Radar_X", "惯导中心至车辆左侧距离:"],
                ["Radar_Y", "惯导中心至车辆前侧距离:"],
                ["Radar_Z", "惯导中心至地面高度:"],
                ["Source_X", "车辆后轴中心至车辆左侧距离:"],
                ["Source_Y", "车辆后轴中心至车辆后侧距离:"],
                ["Source_Z", "车辆后轴中心至地面距离:"],
            ]),
            new Map<string, number>([
                ["Car_W", 0],
                ["Car_L", 0],
                ["Radar_X", 0],
                ["Radar_Y", 0],
                ["Radar_Z", 0],
                ["Source_X", 0],
                ["Source_Y", 0],
                ["Source_Z", 0],
            ]),
        ),
    ]);

    public Tips = [
        '标定说明001.png',
        '标定说明002.png',
        '标定说明003.png',
    ];

    /**
     * 当前标定配置Id
     */
    public Id: Ref<number> = ref(1);

    public Options: ComputedRef<Array<string>> = computed(() => this.Configs.find(config => config.Id == this.Id.value)?.Options ?? []);

    /**
     * 显示标定说明
     */
    public ShowOrigin: Ref<boolean> = ref(false);
    /**
     * 显示标定说明
     */
    public ShowTips: Ref<boolean> = ref(false);
    /**
     * 显示参数标定
     */
    public ShowForms: Ref<boolean> = ref(false);

    private hasInitialized: boolean = false;

    private volume: number = 0;
    private effect: number = 0;

    private watchStopHandles: Array<WatchStopHandle> = [];

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetTransparentBackground();
        TooltipComponent.Default.Clear();

        if (!this.hasInitialized) {
            await this.LoadAsync();
            this.hasInitialized = true;
        }

        const config = this.Configs.find(config => config.Id == this.Id.value)!;
        await GameObjectComponent.Default.ShowAsync(config.InstanceId);
        await OrbitCameraComponent.Default.ApplyConfigAsync(config.ViewId);

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        const config = this.Configs.find(config => config.Id == this.Id.value)!;
        await GameObjectComponent.Default.HideAsync(config.InstanceId);

        lock.Dispose();
    }

    public GetRecord(key: string): number {
        const config = this.Configs.find(config => config.Id == this.Id.value)!;
        return config.Records.get(key) ?? 0;
    }

    private async OnPopupChangedAsync(value: boolean): Promise<void> {
        if (value) {
            this.volume = await PostProcessLayerComponent.Default.AddVolumeAsync();
            this.effect = await PostProcessLayerComponent.Default.AddKawaseBlurAsync(this.volume);
        } else {
            await PostProcessLayerComponent.Default.RemoveEffectAsync(this.volume, this.effect);
            await PostProcessLayerComponent.Default.RemoveVolumeAsync(this.volume);
        }
    }

    private async LoadAsync(): Promise<void> {
        if (import.meta.env.DEV) {
            return;
        }

        const volume = await PostProcessLayerComponent.Default.AddVolumeAsync();
        const effect = await PostProcessLayerComponent.Default.AddKawaseBlurAsync(volume);

        // 加载模型
        const instanceIds = await AssetLoaderComponent.Default.LoadAllAsync(this.Configs.map(config => config.Asset));
        for (let index = 0; index < instanceIds.length; ++index) {
            this.Configs[index].InstanceId = instanceIds[index];
        }

        // 初始化车辆坐标系标定
        const gizmos: ClientMessage.LaserRadarGizmosResponse = await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarGizmosRequest(this.Configs[0].InstanceId));
        this.Configs[0].Origin = gizmos.Origin;
        this.Configs[0].Values.clear();
        this.Configs[0].Values.addRange(gizmos.Values);

        await GameObjectComponent.Default.HideAsync(instanceIds);

        TooltipComponent.Default.Clear();

        await PostProcessLayerComponent.Default.RemoveEffectAsync(volume, effect);
        await PostProcessLayerComponent.Default.RemoveVolumeAsync(volume);
    }

    /**
     * 标定Id发生变化时
     * @param value 值
     */
    private async OnIdChangedAsync(value: number, previous: number): Promise<void> {
        const config = this.Configs.find(config => config.Id == value)!;
        const previousConfig = this.Configs.find(config => config.Id == previous);
        if (import.meta.env.PROD) {
            TooltipComponent.Default.Clear();

            if (previousConfig != undefined) {
                await GameObjectComponent.Default.HideAsync(previousConfig.InstanceId);
            }

            await GameObjectComponent.Default.ShowAsync(config.InstanceId);

            await OrbitCameraComponent.Default.ApplyConfigAsync(config.ViewId);

            this.ShowOrigin.value = config.ShowOrigin;
        }
    }

    private async OnOriginStateChangedAsync(value: boolean): Promise<void> {
        const config = this.Configs.find(config => config.Id == this.Id.value)!;
        config.ShowOrigin = value;
        if (value) {
            await GameObjectComponent.Default.ShowAsync(config.Origin);
        } else {
            await GameObjectComponent.Default.HideAsync(config.Origin);
        }
    }
}

/**
 * 协议解读组件
 */
export class ProtocolComponent {
    private static instance: ProtocolComponent = new ProtocolComponent();
    /**
     * 协议解读组件
     */
    public static get Default(): ProtocolComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public Capacity: number = 8;

    public DataIndex: Ref<number> = ref(0);
    public Datas: UnwrapNestedRefs<Array<Uint8Array>> = reactive([]);

    public Data: Ref<Array<number>> = ref([]);

    public DocumentUrl: Ref<string> = ref('/public/static/LaserRadar/协议解析.pdf');

    public ShowDocument: Ref<boolean> = ref(false);
    public ShowTool: Ref<boolean> = ref(false);

    private watchStopHandles: Array<WatchStopHandle> = [];

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        WebWindowComponent.Default.SetBlackBackground();
        TooltipComponent.Default.Clear();

        if (import.meta.env.DEV) {
            this.Datas.clear();
            this.Datas.addRange([
                new Uint8Array([0x55, 0xAA, 0x4C, 0xF0, 0xDA, 0x02, 0x00, 0xF6, 0x90, 0x39, 0xC2, 0x40, 0x0A, 0x94, 0x3B, 0xC5, 0x8C, 0x9D, 0xBD, 0xBE, 0x7A, 0xD5, 0xBD, 0x11, 0x01, 0xA3, 0xBD, 0xAC, 0xC2, 0x1C, 0x41, 0x74, 0x97, 0xB3, 0x3D, 0x31, 0x80, 0x60, 0x39, 0x33, 0xAE, 0xA0, 0xB8, 0x04, 0x7C, 0xD3, 0x39, 0x89, 0xE8, 0xF1, 0x42, 0x54, 0xF6, 0xE3, 0x47, 0x5C, 0x9C, 0xA0, 0x12, 0xCA, 0x3F, 0x05, 0xBF, 0x7C, 0xC3, 0x02, 0x40, 0x11, 0x49, 0xEC, 0xBE, 0x02, 0x00, 0x00, 0x00, 0xDD, 0xDA, 0x00, 0x00, 0x3B]),
                new Uint8Array([0x55, 0xAA, 0x4C, 0x80, 0x5F, 0x52, 0x00, 0x05, 0xFF, 0x1F, 0x00, 0xBE, 0x11, 0x24, 0xBD, 0x95, 0x97, 0x2D, 0xBD, 0x3C, 0xC1, 0x9D, 0xBD, 0x28, 0xEF, 0xA0, 0xBB, 0x28, 0xB0, 0x1C, 0x41, 0x48, 0x98, 0xDD, 0x3D, 0x3B, 0xB6, 0x2E, 0xB9, 0x07, 0x99, 0x84, 0xB7, 0x7D, 0xB2, 0x5A, 0xBA, 0x0E, 0x61, 0xFA, 0x42, 0x54, 0xF6, 0xE3, 0x47, 0x55, 0x9C, 0xA0, 0x12, 0x5E, 0x41, 0x27, 0xBF, 0xBC, 0xCA, 0x53, 0xBE, 0x4D, 0x3B, 0x25, 0xBD, 0x52, 0x00, 0x00, 0x00, 0x6D, 0x5F, 0x00, 0x00, 0xBA]),
            ]);

            this.Receive(this.Datas[0]);
            this.Receive(this.Datas[1]);
        }

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();
        lock.Dispose();
    }

    public Receive(data: Uint8Array): void {
        if (this.Datas.length > this.Capacity) {
            this.Datas.removeAt(0);
        }
        this.DataIndex.value += 1;
        this.Datas.add(data);
    }

    public Resolve(data: Uint8Array): void {
        this.Data.value.clear();
        this.Data.value.addRange(Array.from(data));
    }

    public Convert(input: string, fromRadix: number, toRadix: number): string {
        return Number.parseInt(input, fromRadix).toString(toRadix);
    }
}

/**
 * 应用界面
 */
export class UseComponent {
    private static instance: UseComponent = new UseComponent();
    /**
     * 应用界面
     */
    public static get Default(): UseComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        this.watchStopHandles.push(watch(this.Choice, this.OnChoicesChangedAsync.bind(this)));
        // this.watchStopHandles.push(watch(this.ShowScene, this.UpdateMapBuildingSettingsAsync.bind(this)));
        // this.watchStopHandles.push(watch(this.ShowPath, this.UpdateMapBuildingSettingsAsync.bind(this)));
        // this.watchStopHandles.push(watch(this.ShowMap, this.UpdateMapBuildingSettingsAsync.bind(this)));
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);

        for (const watchStopHandle of this.watchStopHandles) {
            watchStopHandle();
        }
        this.watchStopHandles.clear();
    }

    public Options: Map<string, string> = new Map([
        ['点云呈现', 'ic_dianyunchengxian'],
        ['激光扫描', 'ic_jiguangsaomiao'],
        ['地图构建', 'ic_ditugoujian'],
    ]);

    public Choice: Ref<string> = ref('点云呈现');

    public ShowScene: Ref<boolean> = ref(true);
    public ShowPath: Ref<boolean> = ref(false);
    public ShowMap: Ref<boolean> = ref(true);

    private watchStopHandles: Array<WatchStopHandle> = [];

    private volume: number = 0;
    private effect: number = 0;

    public async Awake(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarUsageTypeRequest(this.Choice.value));

        lock.Dispose();
    }

    public async Destroy(): Promise<void> {
        const lock = await LaserRadarComponent.Default.WaitAsync();

        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarUsageTypeRequest(''));

        lock.Dispose();
    }

    public async OnChoicesChangedAsync(): Promise<void> {
        await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarUsageTypeRequest(this.Choice.value));
    }

    public async StartBuildMapAsync(): Promise<boolean> {
        const response = await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarStartBuildMapRequest());
        return response.Error == 0;
    }

    public async StopBuildMapAsync(): Promise<boolean> {
        const response = await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarStopBuildMapRequest());
        return response.Error == 0;
    }

    // public async UpdateMapBuildingSettingsAsync(): Promise<boolean> {
    //     const response = await WebSocketComponent.Default.PostMessageAsync(new ClientMessage.LaserRadarMapBuildingSettingsRequest(this.ShowScene.value, this.ShowPath.value, this.ShowMap.value));
    //     return response.Error == 0;
    // }
}